/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <stdlib.h>

#include "iot_list.h"

#define LIST_DEBUG
#ifdef  LIST_DEBUG
#define LST_DBG(fmt, args...)   printf("[LIST_DEBUG][%s|%d]" fmt, __func__, __LINE__, ##args)
#define LST_ERR(fmt, args...)   printf("[LIST_ERROR][%s|%d]" fmt, __func__, __LINE__, ##args)
#else
#define LST_DBG(fmt, args...)   do {} while (0)
#define LST_ERR(fmt, args...)   do {} while (0)
#endif

typedef struct __ListNode {
    void *data;
    int size;
    struct __ListNode *prev;
    struct __ListNode *next;
} ListNode;

typedef struct {
    ListNode *head, *tail;
    int total;
} DLinkList;

static ListNode *ListNodeCreate(void *data, int size)
{
    ListNode *pNode = NULL;
    if (data == NULL || size <= 0) {
        LST_ERR("NULL POINT!\n");
        return pNode;
    }

    pNode = (ListNode *)malloc(sizeof(ListNode));
    if (pNode == NULL) {
        LST_ERR("OOM!\n");
        return NULL;
    }

    pNode->data = malloc(size);
    if (pNode->data == NULL) {
        LST_ERR("OOM!\n");
        free(pNode);
        return NULL;
    }

    memcpy(pNode->data, data, size);
    pNode->size = size;
    pNode->prev = NULL;
    pNode->next = NULL;

    return pNode;
}

static void ListNodeDestroy(ListNode *pNode)
{
    if (pNode == NULL) {
        return;
    }
    if (pNode->data) {
        free(pNode->data);
        pNode->data = NULL;
    }
    free(pNode);
}

static ListNode *ListNodeSearch(ListNode *head, int idx)
{
    ListNode *pNode = head;
    for (int i = 0; i < idx; i++) {
        if (pNode == NULL) {
            break;
        }
        pNode = pNode->next;
    }

    return pNode;
}

static ListNode *ListNodeSearchData(ListNode *head, void *data)
{
    ListNode *pNode = head;

    while (pNode != NULL) {
        if (memcmp(pNode->data, data, pNode->size) == 0) {
            break;
        }
        pNode = pNode->next;
    }

    return pNode;
}

IOT_LIST IoT_ListCreate(void *data, int size)
{
    ListNode *pNode = NULL;
    DLinkList *list = (DLinkList *)malloc(sizeof(DLinkList));
    if (list == NULL) {
        LST_ERR("OOM!\n");
        return NULL;
    }

    pNode = ListNodeCreate(data, size);
    if (pNode != NULL) {
        list->total = 1;
    } else {
        list->total = 0;
    }
    list->head = list->tail = pNode;

    return (DLinkList *)list;
}

int IoT_ListAppend(IOT_LIST mHandle, void *data, int size)
{
    ListNode *pNode = NULL;
    DLinkList *list = (DLinkList *)mHandle;
    if (list == NULL) {
        LST_ERR("NULL POINT! \n");
        return -1;
    }

    pNode = ListNodeCreate(data, size);
    if (pNode == NULL) {
        LST_ERR("ListNodeCreate failed! \n");
        return -1;
    }

    if (list->head == NULL) {
        list->head = list->tail = pNode;
    } else {
        list->tail->next = pNode;
        pNode->prev = list->tail;
        list->tail = pNode;
    }

    list->total++;

    return 0;
}

int IoT_ListUpdate(IOT_LIST mHandle, int idx, void *data, int size)
{
    int retval = 0;
    ListNode *pNode = NULL;
    DLinkList *list = (DLinkList *)mHandle;
    if (list == NULL || data == NULL) {
        LST_ERR("NULL POINT! \n");
        return -1;
    }

    pNode = ListNodeSearch(list->head, idx);
    if (pNode != NULL) {
        if (size != pNode->size) {
            pNode->data = realloc(pNode->data, size);
            pNode->size = size;
        }
        memcpy(pNode->data, data, size);
    } else {
        retval = IoT_ListAppend(mHandle, data, size);
    }

    return retval;
}

int IoT_ListDelete(IOT_LIST mHandle, void *data)
{
    ListNode *pNode = NULL;
    ListNode *prev = NULL;
    ListNode *next = NULL;
    DLinkList *list = (DLinkList *)mHandle;
    if (list == NULL || data == NULL) {
        LST_ERR("NULL POINT! \n");
        return -1;
    }

    pNode = ListNodeSearchData(list->head, data);
    if (pNode == NULL) {
        return -1;
    }
    prev = pNode->prev;
    next = pNode->next;

    if (prev != NULL && next != NULL){
        prev->next = next;
        next->prev = prev;
    } else if (prev == NULL && next != NULL) {
        next->prev = NULL;
        list->head = next;
    } else if (prev != NULL && next == NULL) {
        prev->next = NULL;
        list->tail = prev;
    } else {
        list->head = list->tail = NULL;
    }
    pNode->next = NULL;
    pNode->prev = NULL;
    list->total--;

    ListNodeDestroy(pNode);
    pNode = NULL;

    return 0;
}

int IoT_ListGet(IOT_LIST mHandle, int idx, void *data, int size)
{
    ListNode *pNode;
    DLinkList *list = (DLinkList *)mHandle;
    if (list == NULL) {
        LST_ERR("NULL POINT! \n");
        return -1;
    }

    pNode = ListNodeSearch(list->head, idx);
    if (pNode == NULL || pNode->data == NULL || data == NULL || size < pNode->size) {
        LST_ERR("no such node(%d) \n", idx);
        return -1;
    }

    memcpy(data, pNode->data, pNode->size);

    return 0;
}

int IoT_ListGetSize(IOT_LIST mHandle)
{
    if (mHandle == NULL) {
        return -1;
    }

    return ((DLinkList *)mHandle)->total;
}

void IoT_ListClear(IOT_LIST mHandle)
{
    DLinkList *list = (DLinkList *)mHandle;
    if (list == NULL) {
        return;
    }
    while (list->head) {
        ListNode *pNode = list->head;
        list->head = pNode->next;
        pNode->next = NULL;
        list->head->prev = NULL;

        ListNodeDestroy(pNode);
    }
    list->total = 0;
}

void IoT_ListDestroy(IOT_LIST mHandle)
{
    DLinkList *list = (DLinkList *)mHandle;
    if (list == NULL) {
        return;
    }
    while (list->head) {
        ListNode *pNode = list->head;
        list->head = pNode->next;
        pNode->next = NULL;
        list->head->prev = NULL;

        ListNodeDestroy(pNode);
    }
    list->total = 0;

    free(list);
    list = NULL;
}
